<script setup>
  //   import $ from 'jquery';
  import { ref, watch, onMounted, onBeforeUnmount, toRef } from 'vue'
  import 'select2/dist/js/select2.full'
  import IconRequired from '../IconRequired.vue'
  import Icon from '../Icon.vue'
  import FieldErrorMessage from './FieldErrorMessage.vue'
  import { useField } from 'vee-validate'
  import { uniqueId } from '@/helpers/utils'

  const props = defineProps({
    value: [String, Array], // previously was `value: String`
    id: {
      type: String,
      default: '',
    },
    name: {
      type: String,
      default: '',
    },
    label: {
      type: String,
      default: '',
    },
    options: {
      type: Array,
      default: () => [],
    },
    idKey: {
      type: String,
      default: 'id',
    },
    labelKey: {
      type: String,
      default: 'label',
    },
    placeholder: {
      type: String,
      default: '',
    },
    description: {
      type: String,
      default: '',
    },
    disabled: {
      type: Boolean,
      default: false,
    },
    required: {
      type: Boolean,
      default: false,
    },
    settings: {
      type: Object,
      default: () => {},
    },
    css_class: { type: String, default: '' },
  })
  const emit = defineEmits(['select'])

  // Référence de l'objet select2
  const select2 = ref()
  // Référence de la div englobant l'élément
  const elementRef = ref()
  const nameRef = toRef(props, 'name')
  const optionsRef = toRef(props, 'options')
  // Données de formulaire (d'après le schéma configuré un niveau plus haut, d'où le undefined)
  const {
    value: currentValue,
    handleChange,
    errorMessage,
    meta,
  } = useField(nameRef)

  // Utilitaire
  function setValue(val) {
    if (val instanceof Array) {
      if ((val.length > 0) &&  (val[0] instanceof Object)) {
        val = val.map(dehydrateOption)
      }
      select2.value.val([...val])
    } else {
      select2.value.val([val])
    }
    select2.value.trigger('change')
  }

  function convertObjectToSelect2Option(obj) {
    return {
      id: obj[props.idKey],
      text: obj[props.labelKey],
      data: obj,
    }
  }

  function buildOptions(options) {
    /**
     * Build the options in the select2 format.
     * Handles both inlined option and optgroup options
     */
    if (options.length > 0 && options[0].children !== undefined) {
      return options.map((optiongroup) => {
        return {
          text: optiongroup.text,
          children: optiongroup.children.map(convertObjectToSelect2Option),
        }
      })
    } else {
      return options.map(convertObjectToSelect2Option)
    }
  }
  function buildSettings(propsSettings) {
    const settings = Object.assign({}, propsSettings)
    if (!props.required) {
      settings['allowClear'] = true
    }
    return settings
  }
  function dehydrateOption(obj) {
    return obj[props.idKey]
  }
  // On observe la modification de la value pour mettre à jour
  // la valeur sélectionnée dans le select2
  watch(currentValue, (newValue, prevValue) => setValue(newValue), {
    flush: 'post',
    deep: true,
  })
  watch(
    optionsRef,
    (newOptions) => {
      select2.value.empty()
      const options = buildOptions(newOptions)
      const settings = buildSettings(props.settings)

      select2.value.select2({
        placeholder: props.placeholder,
        ...settings,
        data: options,
      })
      setValue(currentValue.value)
    },
    {
      flush: 'post',
      deep: true,
    }
  )
  onMounted(() => {
    const options = buildOptions(props.options)
    const settings = buildSettings(props.settings)
    select2.value = $(elementRef.value)
      .find('select')
      .select2({
        placeholder: props.placeholder,
        data: options,
        ...settings,
      })
      .on('select2:close', (ev) => {
        const evt = 'scroll.select2'
        $(ev.target).parents().off(evt)
        $(window).off(evt)
      })
      .on('select2:select select2:unselect', (ev) => {
        const evt = 'scroll.select2'
        $(ev.target).parents().off(evt)
        $(window).off(evt)
        handleChange(select2.value.val())
        emit('select', ev['params']['data'])
      })

    setValue(currentValue.value)
  })
  onBeforeUnmount(() => select2.value.select2('destroy'))
  const tagId = uniqueId(props.name)
</script>

<template>
  <div
    class="form-group String"
    ref="elementRef"
    :class="[{ 'has-error': meta.touched && !!errorMessage }, css_class]"
  >
    <label :for="tagId" :class="{ required: required }">
      <Icon name="exclamation-triangle" class="error" />
      {{ label }}
      <IconRequired v-if="required" />
    </label>
    <select
      class="form-control"
      :id="tagId"
      :name="name"
      :disabled="disabled"
    ></select>
    <div class="help-block" v-if="description" v-html="description" />
    <FieldErrorMessage
      v-if="meta.touched && !!errorMessage"
      :message="errorMessage"
    />
  </div>
</template>
